#include "mydb.h"
#include <stdio.h>

#include <memory.h>

const u_int32_t Page_Size = 1024;
const u_int32_t Page_Count = 2048;

static Index_t *index_hdl = NULL;
static Page_t *page_hdl = NULL;
static void *pages = NULL;

static u_int16_t freePage = 2048;

//static functions

static Page_t* allocPages(u_int32_t counter) {
	if (freePage < counter) { // no more page for this save
		return NULL;
	}
	Page_t *ret = NULL;
	u_int32_t i = 0;

	u_int32_t alloced = 0;
	for (; i < Page_Count; i++) {
		if (page_hdl[i].using) {
			continue;
		}

		page_hdl[i].using = 1;
		page_hdl[i].next = ret;
		ret = &page_hdl[i];
		alloced++;
		if (alloced == counter) {
			break;
		}
	}
	freePage = freePage - counter;
	return ret;
}
static void freePages(u_int32_t num) {
	u_int32_t count = 0;
	Page_t *page = &page_hdl[num];
	while (page) {
		page->using = 0;
		count++;
		page = page->next;
	}
	freePage = freePage + count;
}
static Page_t* getPage(u_int32_t num) {
	if (num >= Page_Count) {
		return NULL;
	}

	return &page_hdl[num];
}
//end static functions

int mydb_init() {
	page_hdl = (Page_t*) malloc(sizeof(Page_t) * Page_Count);
	pages = malloc(Page_Size * Page_Count);

	index_hdl = (Index_t*) malloc(sizeof(Index_t) * Page_Count);

	int i = 0;
	for (; i < Page_Count; i++) {
		page_hdl[i].num = i;
		page_hdl[i].using = 0;
		page_hdl[i].datalen = 0;
		page_hdl[i].data = pages + (Page_Size * i);
		page_hdl[i].next = NULL;
	}
	i = 0;
	for (; i < Page_Count; i++) {
		index_hdl[i].pageNo = -1;
		index_hdl[i].key_len = 0;
		index_hdl[i].dat_len = 0;
		memset(index_hdl[i].key, 0, 65535);
		index_hdl[i].next = NULL;
	}
	return 0;
}

int mydb_close() {
	free(pages);
	free(page_hdl);
	free(index_hdl);
	return 0;
}

int mydb_save(const char *key, const void *value, u_int32_t len) {
	size_t keylen = strlen(key);
	if (keylen > (Key_Mx_Len - 1)) {
		return -1;
	}

	u_int16_t length = (u_int16_t) keylen;

	Index_t *target = NULL;
	int var;
	for (var = 0; var < Page_Count; ++var) { // loop in index
		if (index_hdl[var].pageNo == -1) {
			target = &index_hdl[var];
			break;
		}

		if (index_hdl[var].key_len != length) {
			continue;
		}
		int ret = strncmp(index_hdl[var].key, key, keylen);
		if (ret != 0) {
			continue;
		}
		target = &index_hdl[var];
		break;
	}

	if (target == NULL) {
		//TODO replace
		return -1;
	}
	if (target->pageNo != -1) {
		freePages(target->pageNo);
	}

	u_int32_t val = len / Page_Size;
	u_int32_t val2 = len % Page_Size;
	if (val2 != 0) {
		val++;
	}
	Page_t *newPage = allocPages(val);
	if (newPage == NULL) {
		return -1;
	}
	Page_t *temp = newPage;
	u_int32_t offset = 0;
	while (temp) {
		if (temp->next == NULL) {
			memcpy(temp->data, (value + offset), val2 != 0 ? val2 : Page_Size);
			temp->datalen = val2 != 0 ? val2 : Page_Size;
			break;
		} else {
			memcpy(temp->data, (value + offset), Page_Size);
			temp->datalen = Page_Size;
			offset = offset + Page_Size;
			temp = temp->next;
		}
	}
	target->pageNo = newPage->num;
	target->key_len = keylen;
	target->dat_len = len;
//	target->key =
	strncpy(target->key, key, keylen);
	target->key[length] = '\0';

	return 0;
}

int mydb_del(const char *key) {
	size_t keylen = strlen(key);
	u_int16_t length = (u_int16_t) keylen;

	Index_t *target = NULL;
	int var;
	for (var = 0; var < Page_Count; ++var) { // loop in index
		if (index_hdl[var].pageNo == -1) {
			continue;
		}

		if (index_hdl[var].key_len != length) {
			continue;
		}
		int ret = strncmp(index_hdl[var].key, key, keylen);
		if (ret != 0) {
			continue;
		}
		target = &index_hdl[var];
		break;
	}

	if (target == NULL) {
		//TODO replace
		return -1;
	}
	freePages(target->pageNo);
	target->pageNo = -1;
	return 0;

}
Index_t* mydb_query(const char *key) {
	size_t keylen = strlen(key);
	u_int16_t length = (u_int16_t) keylen;

	Index_t *target = NULL;
	int var;
	for (var = 0; var < Page_Count; ++var) { // loop in index
		if (index_hdl[var].pageNo == -1) {
			continue;
		}

		if (index_hdl[var].key_len != length) {
			continue;
		}
		int ret = strncmp(index_hdl[var].key, key, keylen);
		if (ret != 0) {
			continue;
		}
		target = &index_hdl[var];
		break;
	}

	return target;
}
int mydb_queryDat(const Index_t *index, void *value) {
	Page_t *temp = getPage(index->pageNo);
	if (temp == NULL) {
		return -1;
	}
	u_int32_t offset = 0;
	while (temp) {
		if (temp->next == NULL) {
			memcpy(value + offset, (temp->data), temp->datalen);
			break;
		} else {
			memcpy(value + offset, (temp->data), temp->datalen);
			offset = offset + Page_Size;
			temp = temp->next;
		}
	}
	return 0;
}

/***something about index**/

Index_t* buildIndex(char *indexfile) {
	return 0;
}
int releaseIndex(Index_t *index) {
	return 0;
}

/**
 * 0 no this key
 * >0 use pageNo param
 */
int queryIndex(char *key, u_int32_t *pageNo) {
	return 0;
}
int newIndex(char *key, u_int32_t pageNo) {
	return 0;
}
int removeIndex(char *key) {
	return 0;
}

/***something about store**/
u_int32_t saveData(char *data, u_int16_t len) {
	return 0;
}
int removeData(u_int32_t pageNo) {
	return 0;
}
u_int16_t getData(u_int16_t pageNo, char *data) {
	return 0;
}
